package com.yeqiangwei.commons.ibatis;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.yeqiangwei.commons.util.Pagination;
import com.yeqiangwei.commons.util.Pagination.Order;
import com.yeqiangwei.commons.util.DateTimeUtils;
import com.yeqiangwei.commons.util.StringHelper;
import com.yeqiangwei.commons.util.ValidatorHelper;


public abstract class GenericIBatisDAOTemplate<PK extends Serializable, T extends PKModel<PK>> extends SqlMapClientDaoSupport {

	private static Log log = null;
	
	protected Log getLog() {
		if(log == null) {
			log = LogFactory.getLog(this.getClass());
		}
		return log;
	}
	
	final static Map<String, String> ALIAS_MAP = new HashMap<String, String>();
	
	protected static final String ASC = "ASC";
	protected static final String DESC = "DESC";
	protected static final String ORDER_STR = "orderby";
	protected static final String MAX_RESULTS = "maxResults";
	protected static final String START_ROW_NUM = "startRowNum";
	protected static final String MAX_ROW_NUM = "maxRowNum";
	protected static final String END_ROW_NUM = "endRowNum";
	
	protected abstract Class<?> getObjectClass();

	protected abstract String getTableName();
	
	protected String getNamespace() {
		return getObjectClass().getName();
	}
	
	protected String getObjectAlias(){
		String objectClassName = getObjectClass().getName();
		String tableName = ALIAS_MAP.get(objectClassName); 
		if(tableName == null){
			tableName = StringHelper.substring(objectClassName,
					objectClassName.lastIndexOf(".") + 1, 
					objectClassName.length(),
					null);
			ALIAS_MAP.put(objectClassName, tableName);
		}
		return tableName;
	}
	
	protected enum FieldEnum {
		ID("id")
		, NAME("name")
		;
		FieldEnum(String val) {
			this.val = val;
		}
		String val;
		public String getValue() {
			return this.val;
		}
	}
	
	public List<T> findAll(Pagination pagination) {
		long stime = DateTimeUtils.now();
		List<T> list = null;
		Map<String, Object> map = new HashMap<String, Object>();
		if(pagination != null) {
			List<Order> orderList = pagination.getOrderList();
			if(orderList != null){
				StringBuilder orderStr = new StringBuilder();
				for(int i = 0; i < orderList.size(); i++) {
					Order order = orderList.get(i);
					orderStr.append(order.getName());
					orderStr.append(" ");
					if(order.getAscending()) {
						orderStr.append(ASC);
					} else {
						orderStr.append(DESC);
					}
					if((i+1) < orderList.size()) {
						orderStr.append(", ");
					}
				}
				map.put(ORDER_STR, orderStr.toString());
			} else {
				map.put(ORDER_STR, " id desc");
			}
			map.put(START_ROW_NUM, pagination.getStartRows());
			map.put(MAX_ROW_NUM, pagination.getMaxResults());
			map.put(END_ROW_NUM, pagination.getStartRows() + pagination.getMaxResults());
		}
		list = getSqlMapClientTemplate().queryForList(getNamespace() + ".findAll", map);
		getLog().info("findAll(Pagination pagination): proceed in " + (DateTimeUtils.now() - stime));
		return list;
	}
	
	public int countAll(Map map) {
		long stime = DateTimeUtils.now();
		Integer i = null;
		if (map == null) {
			i = (Integer) getSqlMapClientTemplate().queryForObject(getNamespace() + ".countAll");
		} else {
			i = (Integer) getSqlMapClientTemplate().queryForObject(getNamespace() + ".countAll", map);
		}
		getLog().info("countAll(Map map): proceed in " + (DateTimeUtils.now() - stime));
		return i;
	}
	

	public void create(T t) throws DAOException {
		long stime = DateTimeUtils.now();
		if(t == null){
			return ;
		}
		PK id = (PK) getSqlMapClientTemplate().insert(getNamespace() + ".create", t);
		if(t.getId() == null){
			t.setId(id);
		}
		getLog().info("create(T t): proceed in " + (DateTimeUtils.now() - stime));
	}

	public void createOrUpdate(T model) throws DAOException { 
		long stime = DateTimeUtils.now();
		if(model == null){
			return ;
		}
		if(ValidatorHelper.isEmpty(model.getId())){
			update(model);
		} else {
			create(model);
		}
		getLog().info("createOrUpdate(T model): proceed in " + (DateTimeUtils.now() - stime));
	}

	//
	public T getById(PK id) {
		long stime = DateTimeUtils.now();
		if(id == null) {
			return null;
		}
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(FieldEnum.ID.getValue(), id);
		T model = getByPropertys(map);
		getLog().info("getById(PK id): proceed in " + (DateTimeUtils.now() - stime));
		return model;
	}

	public T getByProperty(String key, Object value) {
		long stime = DateTimeUtils.now();
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(key, value);
		T model = getByPropertys(map);
		getLog().info("getByProperty(String key, Object value): proceed in " + (DateTimeUtils.now() - stime));
		return model;
	}

	public T getByPropertys(Map<String, Object> map) {
		long stime = DateTimeUtils.now();
		T model = (T) getSqlMapClientTemplate()
		.queryForObject(getNamespace() + ".getByProperties", map);
		getLog().info("getByPropertys(Map<String, Object> map): proceed in " + (DateTimeUtils.now() - stime));
		return model;
	}

	public List<T> findByPropertys(Map<String, Object> map) {
		long stime = DateTimeUtils.now();
		Pagination pagination = (Pagination) map.get("pagination");
		if(pagination != null) {
			map.put(START_ROW_NUM, pagination.getStartRows());
			map.put(MAX_ROW_NUM, pagination.getMaxResults());
			map.put(END_ROW_NUM, pagination.getStartRows() + pagination.getMaxResults());
			List<Order> orderList = pagination.getOrderList();
			if(ValidatorHelper.isNotEmpty(orderList)) {
				StringBuilder sb = new StringBuilder();
				for(int i = 0; i < orderList.size(); i++) {
					Order order = orderList.get(i);
					sb.append(order.getName());
					if(order.getAscending()) {
						sb.append(" ASC");
					} else {
						sb.append(" DESC");
					}
					if(i != (orderList.size()-1)) {
						sb.append(",");
					}
				}
				map.put(ORDER_STR, sb.toString());
			}
		}
		List<T> list = getSqlMapClientTemplate().queryForList(getNamespace() + ".findByProperties", map);
		getLog().info("findByPropertys(Map<String, Object> map): proceed in " + (DateTimeUtils.now() - stime));
		return list;
	}
	
	public void remove(T t) {
		if(t == null){
			return ;
		}
		getSqlMapClientTemplate().delete(getNamespace() + ".remove", t.getId());
	}

	public void removeByProperty(String property, Object value) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(property, value);
		getSqlMapClientTemplate().delete(getNamespace() + ".removeByProperty", map);
	}

	public void update(T model) {
		if(model == null){
			return ;
		}
		getSqlMapClientTemplate().update(getNamespace() + ".update", model);
	}
}
